package com.example.demo.common;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 默认情况下，所有的查询都走从库，插入/修改/删除走主库。我们通过方法名来区分操作类型（CRUD）
 * <p>
 * 需要注意的是所有能拦截的方法都在这儿拦截，所以方法名不对不会拦截，会使用默认数据源 master
 */
@Aspect     //作用是把当前类标识为一个切面供容器读取
@Component
@Slf4j
public class DataSourceAop {
    // 切点：切点表达式、 拦截Master、select开头的方法、find开头的方法、get...
    // 也可以像下面那样写，放到通知里
/*    @Pointcut("!@annotation(com.example.demo.annotation.Master) " +
            "&& (execution(* com.example.demo.service.*.select*(..)) " +
            "|| execution(* com.example.demo.service..*.find*(..))" +
            "|| execution(* com.example.demo.service..*.get*(..)))")
    public void readPointcut() {
    }*/

    // 切点：切点表达式、拦截Master、save、add...等开头的方法
    @Pointcut("@annotation(com.example.demo.annotation.Master) " +
            "|| execution(* com.example.demo.service..*.save*(..)) " +
            "|| execution(* com.example.demo.service..*.add*(..)) " +
            "|| execution(* com.example.demo.service..*.update*(..)) " +
            "|| execution(* com.example.demo.service..*.edit*(..)) " +
            "|| execution(* com.example.demo..*.delete*(..)) " +
            "|| execution(* com.example.demo..*.remove*(..))")
    public void writePointcut() {
    }

    @Before(value = "!@annotation(com.example.demo.annotation.Master) " +
            "&& (execution(* com.example.demo.service.*.select*(..)) " +
            "|| execution(* com.example.demo.service..*.find*(..))" +
            "|| execution(* com.example.demo.service..*.get*(..)))")
    public void read() {
        System.out.println("读操作");
        DBContextHolder.slave();
    }


    // before: 前置通知
    // after：后置通知
    // around：环绕通知
    // AfterThrowing ：异常通知
    // AfterRunning：返回通知
    @Before("writePointcut()")
    public void write() {
        System.out.println("写操作");
        DBContextHolder.master();
    }


    /**
     * 另一种写法：if...else...  判断哪些需要读从数据库，其余的走主数据库
     */
//    @Before("execution(* com.cjs.example.service.impl.*.*(..))")
//    public void before(JoinPoint jp) {
//        String methodName = jp.getSignature().getName();
//
//        if (StringUtils.startsWithAny(methodName, "get", "select", "find")) {
//            DBContextHolder.slave();
//        }else {
//            DBContextHolder.master();
//        }
//    }


// aop相关知识：
//    在Spring AOP中，有3个常用的概念，Advices、Pointcut、Advisor，解释如下，
//
//    Advices：表示一个method执行前或执行后的动作。
//
//    Pointcut：表示根据method的名字或者正则表达式去拦截一个method。
//
//    Advisor：Advice和Pointcut组成的独立的单元，并且能够传给proxy factory 对象。
}
